package cn.stylefeng.guns.modular.fin.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.stylefeng.guns.modular.app.controller.market.Kline;
import cn.stylefeng.guns.modular.base.state.Constant;
import cn.stylefeng.guns.modular.base.state.ProConst;
import cn.stylefeng.guns.modular.base.util.RedisUtil;
import cn.stylefeng.guns.modular.fin.entity.*;
import cn.stylefeng.guns.modular.fin.mapper.ProfitMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ProfitService extends ServiceImpl<ProfitMapper, Profit> {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private WalletService walletService;
    @Autowired
    private CurrencyService currencyService;

    @Autowired
    private CashflowService cashflowService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private PlatformTransactionManager transactionManager;
    @Autowired
    private ContractService contractService;
    @Autowired
    private FinOptionService finOptionService;

//    // 记录累计盈亏
//    @Scheduled(cron = "0 0 * * * ?")
//    public void recordProfit() {
//        DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
//        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
//
//        try {
//            // 昨日00:00:00
//            Date yesterdayBeginTime = DateUtil.beginOfDay(DateUtil.yesterday());
//            // 昨日23:59:59
//            Date yesterdayEndTime = DateUtil.endOfDay(DateUtil.yesterday());
//
//            // 获取昨天的所有盈亏记录, 用于判断是否已经记录过 yyyy-MM-dd
//            List<Profit> yesterdayProfitList = this.list(new LambdaQueryWrapper<Profit>()
//                    .eq(Profit::getProfitDate, DateUtil.yesterday().toDateStr()));
//
//
//            // 钱包账户
//            // String walletCode = ProConst.WalletBigType.WALLET.code;
//            // List<Profit> yesterdayWalletProfitList = yesterdayProfitList.stream()
//            //         .filter(profit -> walletCode.equals(profit.getWalletType()))
//            //         .collect(Collectors.toList());
//            // handleWallet(yesterdayBeginTime, yesterdayEndTime, walletCode, yesterdayWalletProfitList);
//
//            // 现货钱包
//            String currencyCode = ProConst.WalletBigType.CURRENCY.code;
//            List<Profit> yesterdayCurrencyProfitList = yesterdayProfitList.stream()
//                    .filter(profit -> currencyCode.equals(profit.getWalletType()))
//                    .collect(Collectors.toList());
//            handleCurrency(yesterdayBeginTime, yesterdayEndTime, currencyCode, yesterdayCurrencyProfitList);
//
////            // 永续钱包 (计算usdt)
////            String contractCode = ProConst.WalletBigType.CONTRACT.code;
////            List<Profit> yesterdayContractProfitList = yesterdayProfitList.stream()
////                    .filter(profit -> contractCode.equals(profit.getWalletType()))
////                    .collect(Collectors.toList());
////            handleContract(contractCode, yesterdayContractProfitList);
//
//
//            // 期权
////            String optionCode = ProConst.WalletBigType.OPTION.code;
////            List<Profit> yesterdayOptionProfitList = yesterdayProfitList.stream()
////                    .filter(profit -> optionCode.equals(profit.getWalletType()))
////                    .collect(Collectors.toList());
////            handleOption(optionCode, yesterdayOptionProfitList);
//
//
//            // 提交事务
//            transactionManager.commit(status);
//        } catch (Exception e) {
//            log.error("记录昨日收益失败:", e);
//            // 回滚事务
//            transactionManager.rollback(status);
//        }
//    }

    private void handleOption(String optionCode, List<Profit> yesterdayOptionProfitList) {
        // 1.查询所有人的期权账户
        List<FinOption> optionList = finOptionService.list(new LambdaUpdateWrapper<FinOption>()
                .eq(FinOption::getDel, "N"));

        if (CollUtil.isEmpty(optionList)) {
            return;
        }

        String beforeYesterday = DateUtil.offsetDay(new Date(), -2).toDateStr(); // 前天

        List<Profit> beforeYesterdayProfitList = this.list(new LambdaQueryWrapper<Profit>()
                .eq(Profit::getProfitDate, beforeYesterday)
                .eq(Profit::getWalletType, optionCode)
        );

        // 按用户分组
        Map<Long, List<FinOption>> userGroupMap = optionList.stream()
                .collect(Collectors.groupingBy(FinOption::getMemberId));

        List<Profit> insertProfitList = new ArrayList<>();

        // 遍历用户分组，计算盈亏
        for (Map.Entry<Long, List<FinOption>> entry : userGroupMap.entrySet()) {
            Long memberId = entry.getKey();
            List<FinOption> optionDataList = entry.getValue();

            for (FinOption item : optionDataList) {
                // 判断是否已经添加过
                boolean isExist = judgementExist(memberId, optionCode, item.getType(), yesterdayOptionProfitList);
                if (isExist) {
                    continue;
                }


                // 找出前天记录的信息
                List<Profit> filterList = beforeYesterdayProfitList.stream().filter(e -> e.getMemberId().equals(memberId) && e.getCoinType().equals(item.getType())
                        && DateUtil.format(e.getProfitDate(), "yyyy-MM-dd").equals(beforeYesterday)).collect(Collectors.toList());
                BigDecimal beforeYesterdayPrice = BigDecimal.valueOf(0);
                if (CollUtil.isNotEmpty(filterList)) {
                    Profit profit = filterList.get(0);
                    beforeYesterdayPrice = new BigDecimal(profit.getLastPrice()); // 前天的资产
                }

                BigDecimal currentPrice = item.getUsedPrice().add(item.getLockedPrice());
                if (currentPrice.compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }

                // 如果beforeYesterdayWorthPrice为0,那盈亏就等于0
                BigDecimal yesterdayProfit;
                if (beforeYesterdayPrice.compareTo(BigDecimal.ZERO) == 0) {
                    yesterdayProfit = BigDecimal.ZERO;
                } else {
                    // 昨日盈亏=现在的权益资产 - 前天的资产
                    yesterdayProfit = currentPrice.subtract(beforeYesterdayPrice).setScale(4, RoundingMode.DOWN);
                }

                // String debugStr = "合约钱包 --- 币种:{}, 用户:{}, \n" +
                //         "昨日收益:{}$\n";
                // System.err.println(StrUtil.format(debugStr,
                //         memberId, item.getType(),
                //         yesterdayProfit.toString()));

                // 记录到数据库中
                insertProfitList.add(new Profit()
                        .setMemberId(memberId)
                        .setWalletType(optionCode)
                        .setCoinType(item.getType())
                        .setProfitPrice(yesterdayProfit.toString())
                        .setProfitDate(DateUtil.yesterday())
                        .setCreateTime(new Date())
                        .setLastPrice(currentPrice.toString())
                );

            }
        }

        if (CollUtil.isNotEmpty(insertProfitList)) {
            this.saveBatch(insertProfitList);
        }

    }

    private void handleContract(String contractCode, List<Profit> yesterdayContractProfitList) {
        // 结束时间的资产 - 开始时间的资产
        // 1. 查询所有用户的合约账户
        List<Contract> contractList = contractService.list(new LambdaUpdateWrapper<Contract>()
                .eq(Contract::getDel, "N"));

        if (CollUtil.isEmpty(contractList)) {
            return;
        }

        String beforeYesterday = DateUtil.offsetDay(new Date(), -2).toDateStr(); // 前天

        List<Profit> beforeYesterdayProfitList = this.list(new LambdaQueryWrapper<Profit>()
                .eq(Profit::getProfitDate, beforeYesterday)
                .eq(Profit::getWalletType, contractCode)
        );

        // 2.按用户分组
        Map<Long, List<Contract>> userGroupMap = contractList.stream()
                .collect(Collectors.groupingBy(Contract::getMemberId));

        List<Profit> insertProfitList = new ArrayList<>();

        // 3.遍历用户分组，计算盈亏
        for (Map.Entry<Long, List<Contract>> entry : userGroupMap.entrySet()) {
            Long memberId = entry.getKey();
            List<Contract> contractDataList = entry.getValue();

            for (Contract item : contractDataList) {
                // 判断是否已经添加过
                boolean isExist = judgementExist(memberId, contractCode, item.getType(), yesterdayContractProfitList);
                if (isExist) {
                    continue;
                }

                Contract contractObj = (Contract) redisUtil.get(String.format(Constant.CONTRACT_CODE, item.getType()) + memberId);
                if (contractObj == null) {
                    continue;
                }

                // 找出前天记录的信息
                List<Profit> filterList = beforeYesterdayProfitList.stream().filter(e -> e.getMemberId().equals(memberId) && e.getCoinType().equals(item.getType())
                        && DateUtil.format(e.getProfitDate(), "yyyy-MM-dd").equals(beforeYesterday)).collect(Collectors.toList());
                BigDecimal beforeYesterdayWorthPrice = BigDecimal.valueOf(0);
                if (CollUtil.isNotEmpty(filterList)) {
                    Profit profit = filterList.get(0);
                    beforeYesterdayWorthPrice = new BigDecimal(profit.getLastPrice()); // 前天的资产
                }


                BigDecimal currentWorthPrice = contractObj.getWorthPrice();
                if (currentWorthPrice.compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }


                // 如果beforeYesterdayWorthPrice为0,那盈亏就等于0
                BigDecimal yesterdayProfit;
                if (beforeYesterdayWorthPrice.compareTo(BigDecimal.ZERO) == 0) {
                    yesterdayProfit = BigDecimal.ZERO;
                } else {
                    // 昨日盈亏=现在的权益资产 - 前天的资产
                    yesterdayProfit = currentWorthPrice.subtract(beforeYesterdayWorthPrice).setScale(4, RoundingMode.DOWN);
                }


                // String debugStr = "合约钱包 --- 币种:{}, 用户:{}, \n" +
                //         "昨日收益:{}$\n";
                // System.err.println(StrUtil.format(debugStr,
                //         memberId, item.getType(),
                //         yesterdayProfit.toString()));

                // 记录到数据库中
                insertProfitList.add(new Profit()
                        .setMemberId(memberId)
                        .setWalletType(contractCode)
                        .setCoinType(item.getType())
                        .setProfitPrice(yesterdayProfit.toString())
                        .setProfitDate(DateUtil.yesterday())
                        .setCreateTime(new Date())
                        .setLastPrice(currentWorthPrice.toString())
                );
            }
        }

        if (CollUtil.isNotEmpty(insertProfitList)) {
            this.saveBatch(insertProfitList);
        }

    }

    private void handleCurrency(Date yesterdayBeginTime, Date yesterdayEndTime, String currencyCode, List<Profit> yesterdayCurrencyProfitList) {
        // 查询所有用户的现货账户
        List<Currency> currencyList = currencyService.list(new LambdaUpdateWrapper<Currency>()
                .eq(Currency::getDel, "N"));
        if (CollUtil.isEmpty(currencyList)) {
            return;
        }

        // 按用户分组
        Map<Long, List<Currency>> userGroupMap = currencyList.stream()
                .collect(Collectors.groupingBy(Currency::getMemberId));

        List<Profit> insertProfitList = new ArrayList<>();

        // 遍历用户分组，计算盈亏
        for (Map.Entry<Long, List<Currency>> entry : userGroupMap.entrySet()) {
            Long memberId = entry.getKey();
            List<Currency> currencyDataList = entry.getValue();

            for (Currency item : currencyDataList) {
                // 判断是否已经添加过
                boolean isExist = judgementExist(memberId, currencyCode, item.getType(), yesterdayCurrencyProfitList);
                if (isExist) {
                    continue;
                }

                // 昨天开始时间的币种资产
                Cashflow beginTimeCashFlow = getCashflow(yesterdayBeginTime, memberId, item.getType(), currencyCode);
                if (beginTimeCashFlow == null || beginTimeCashFlow.getAfterPrice().compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }

                // 昨天结束时间的币种资产
                Cashflow endTimeCashFlow = getCashflow(yesterdayEndTime, memberId, item.getType(), currencyCode);

                // 如果有空, 就跳过
                if (endTimeCashFlow == null) {
                    continue;
                }

                List<Kline> klineList = Convert.toList(Kline.class, redisUtil.get(Constant.KINE + item.getType() + "/USDT" + Constant._1DAY));
                if (CollUtil.isEmpty(klineList)) {
                    continue;
                }

                // 按id(时间戳) long 倒序
                klineList.sort((o1, o2) -> Math.toIntExact(o2.getId() - o1.getId()));

                // 取第二个就是昨天
                Kline kline = klineList.get(1);
                if (!DateUtil.date(kline.getId() * 1000).toDateStr().equals(DateUtil.yesterday().toDateStr())) {
                    // throw new RuntimeException("获取" + item.getType() + "/USDT 昨日K线失败");
                    log.error("获取" + item.getType() + "/USDT 昨日K线失败");
                    continue;
                }

                // open就是昨日0点的价格 close就是昨日23:59:59的价格
                BigDecimal beginPrice = BigDecimal.valueOf(kline.getOpen());
                BigDecimal endPrice = BigDecimal.valueOf(kline.getClose());

                // 净划转和净充值 (数量)
                BigDecimal transfersAsset = this.getTransfersAsset(yesterdayBeginTime, yesterdayEndTime, memberId, item.getType(), currencyCode);

                // 0点资产 = 0点的币数 * 0点的价格
                BigDecimal beginAsset = beginTimeCashFlow.getAfterPrice().multiply(beginPrice).setScale(4, RoundingMode.DOWN);
                // 23:59:59资产 = (23:59:59的币数 - 净划转和净充值) * 23:59:59的价格 保留10位
                BigDecimal endAsset = (endTimeCashFlow.getAfterPrice().subtract(transfersAsset)).multiply(endPrice).setScale(4, RoundingMode.DOWN);

                // 昨日收益 = 23:59:59资产 - 0点资产
                BigDecimal yesterdayProfit = endAsset.subtract(beginAsset).setScale(4, RoundingMode.DOWN);

                // String debugStr = "现货钱包 --- 币种:{}, 用户:{}, \n" +
                //         "{} 币数:{} 资产:{}$ 单价:{}\n" +
                //         "{} 币数:{} 资产:{}$ 单价:{}\n" +
                //         "昨日净划转和净充值-数量:{} \n" +
                //         "昨日收益:{}$\n";
                // System.err.println(StrUtil.format(debugStr,
                //         memberId, item.getType(),
                //         yesterdayBeginTime, beginTimeCashFlow.getAfterPrice(), beginAsset.toString(), kline.getOpen(),
                //         yesterdayEndTime, endTimeCashFlow.getAfterPrice(), endAsset.toString(), kline.getClose(),
                //         transfersAsset.toString(),
                //         yesterdayProfit.toString()));

                // 记录到数据库中
                insertProfitList.add(new Profit()
                        .setMemberId(memberId)
                        .setWalletType(currencyCode)
                        .setCoinType(item.getType())
                        .setProfitPrice(yesterdayProfit.toString())
                        .setProfitDate(DateUtil.yesterday())
                        .setCreateTime(new Date())
                        .setLastPrice(endAsset.toString())

                );
            }
        }

        if (CollUtil.isNotEmpty(insertProfitList)) {
            this.saveBatch(insertProfitList);
        }

    }

    private void handleWallet(Date yesterdayBeginTime, Date yesterdayEndTime, String walletCode, List<Profit> yesterdayWalletProfitList) {
        // 1. 查询所有用户的钱包账户
        List<Wallet> walletList = walletService.list(new LambdaUpdateWrapper<Wallet>()
                .eq(Wallet::getDel, "N"));
        if (CollUtil.isEmpty(walletList)) {
            return;
        }

        // 2.按用户分组
        Map<Long, List<Wallet>> userGroupMap = walletList.stream()
                .collect(Collectors.groupingBy(Wallet::getMemberId));

        List<Profit> insertProfitList = new ArrayList<>();

        // 3.遍历用户分组，计算盈亏
        // 昨日盈亏 = 昨日23:59:59资产 - 昨日00:00:00资产 - 昨日的净划转和净充值
        for (Map.Entry<Long, List<Wallet>> entry : userGroupMap.entrySet()) {
            Long memberId = entry.getKey();
            List<Wallet> walletDataList = entry.getValue();

            for (Wallet item : walletDataList) {
                //  -- 用户id 钱包类型 币种 日期 唯一
                //     unique (member_id, wallet_type, coin_type, profit_date)

                // 判断是否已经添加过
                boolean isExist = judgementExist(memberId, walletCode, item.getType(), yesterdayWalletProfitList);
                if (isExist) {
                    continue;
                }


                // 昨天开始时间的币种资产
                Cashflow beginTimeCashFlow = getCashflow(yesterdayBeginTime, memberId, item.getType(), walletCode);
                if (beginTimeCashFlow == null || beginTimeCashFlow.getAfterPrice().compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }

                // 昨天结束时间的币种资产
                Cashflow endTimeCashFlow = getCashflow(yesterdayEndTime, memberId, item.getType(), walletCode);

                // 如果有空, 就跳过
                if (endTimeCashFlow == null) {
                    continue;
                }

                List<Kline> klineList = Convert.toList(Kline.class, redisUtil.get(Constant.KINE + item.getType() + "/USDT" + Constant._1DAY));
                if (CollUtil.isEmpty(klineList)) {
                    continue;
                }

                // 按id(时间戳) long 倒序
                klineList.sort((o1, o2) -> Math.toIntExact(o2.getId() - o1.getId()));

                // 取第二个就是昨天
                Kline kline = klineList.get(1);
                if (!DateUtil.date(kline.getId() * 1000).toDateStr().equals(DateUtil.yesterday().toDateStr())) {
                    throw new RuntimeException("获取" + item.getType() + "/USDT 昨日K线失败");
                }

                // open就是昨日0点的价格 close就是昨日23:59:59的价格
                BigDecimal beginPrice = BigDecimal.valueOf(kline.getOpen());
                BigDecimal endPrice = BigDecimal.valueOf(kline.getClose());

                // 净划转和净充值 (数量)
                BigDecimal transfersAsset = this.getTransfersAsset(yesterdayBeginTime, yesterdayEndTime, memberId, item.getType(), walletCode);

                // 0点资产 = 0点的币数 * 0点的价格
                BigDecimal beginAsset = beginTimeCashFlow.getAfterPrice().multiply(beginPrice).setScale(4, RoundingMode.DOWN);
                // 23:59:59资产 = (23:59:59的币数 - 净划转和净充值) * 23:59:59的价格 保留10位小数
                BigDecimal endAsset = (endTimeCashFlow.getAfterPrice().subtract(transfersAsset)).multiply(endPrice).setScale(4, RoundingMode.DOWN);


                // 昨日收益 = 23:59:59资产 - 0点资产
                BigDecimal yesterdayProfit = endAsset.subtract(beginAsset).setScale(4, RoundingMode.DOWN);


                // String debugStr = "资金钱包 --- 币种:{}, 用户:{}, \n" +
                //         "{} 币数:{} 资产:{}$ 单价:{}\n" +
                //         "{} 币数:{} 资产:{}$ 单价:{}\n" +
                //         "昨日净划转和净充值-数量:{} \n" +
                //         "昨日收益:{}$\n";
                // System.err.println(StrUtil.format(debugStr,
                //         memberId, item.getType(),
                //         yesterdayBeginTime, beginTimeCashFlow.getAfterPrice(), beginAsset.toString(), kline.getOpen(),
                //         yesterdayEndTime, endTimeCashFlow.getAfterPrice(), endAsset.toString(), kline.getClose(),
                //         transfersAsset.toString(),
                //         yesterdayProfit.toString()));

                // 记录到数据库中
                insertProfitList.add(new Profit()
                        .setMemberId(memberId)
                        .setWalletType(walletCode)
                        .setCoinType(item.getType())
                        .setProfitPrice(yesterdayProfit.toString())
                        .setProfitDate(DateUtil.yesterday())
                        .setCreateTime(new Date())
                        .setLastPrice(endAsset.toString())
                );
            }
        }

        if (CollUtil.isNotEmpty(insertProfitList)) {
            this.saveBatch(insertProfitList);
        }


    }

    // 判断是否记录过
    private boolean judgementExist(Long memberId, String walletCode, String coinType, List<Profit> yesterdayWalletProfitList) {
        String yesterday = DateUtil.yesterday().toDateStr(); // yyyy-MM-dd
        for (Profit profit : yesterdayWalletProfitList) {
            if (profit.getMemberId().equals(memberId) && profit.getWalletType().equals(walletCode) && profit.getCoinType().equals(coinType) && DateUtil.format(profit.getProfitDate(), "yyyy-MM-dd").equals(yesterday)) {
                return true;
            }
        }
        return false;
    }

    // 获取昨日的净划转和净充值
    private BigDecimal getTransfersAsset(Date yesterdayBeginTime, Date yesterdayEndTime, Long memberId, String coinType, String walletType) {
        BigDecimal transfersAsset = BigDecimal.ZERO;
        LambdaQueryWrapper<Cashflow> qw = new LambdaQueryWrapper<Cashflow>()
                .eq(Cashflow::getMemberId, memberId)
                .eq(Cashflow::getWalletType, walletType)
                .eq(Cashflow::getFlowCoin, coinType)
                .in(Cashflow::getFlowType, ProConst.CashFlowTypeEnum.PLATFORM_ADD.getCode(), ProConst.CashFlowTypeEnum.CONVERT_IN.getCode())
                .between(Cashflow::getCreateTime, yesterdayBeginTime, yesterdayEndTime)
                .orderByDesc(Cashflow::getCreateTime);
        List<Cashflow> cashflowList = cashflowService.list(qw);

        if (CollUtil.isNotEmpty(cashflowList)) {
            transfersAsset = cashflowList.stream()
                    .map(Cashflow::getFlowPrice)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        return transfersAsset;
    }

    private Cashflow getCashflow(Date dateTime, Long memberId, String coinType, String walletType) {
        return cashflowService.getOne(new LambdaQueryWrapper<Cashflow>()
                .eq(Cashflow::getMemberId, memberId)
                .eq(Cashflow::getWalletType, walletType)
                .eq(Cashflow::getFlowCoin, coinType)
                .le(Cashflow::getCreateTime, dateTime)
                .orderByDesc(Cashflow::getCreateTime)
                .last("limit 1")
        );
    }


    public static void main(String[] args) {
        String theDayBeforeYesterday = DateUtil.offsetDay(new Date(), -2).toDateStr();

        System.out.println(theDayBeforeYesterday);
    }
}
